package endpoint

import (
	"center/db"
	"center/db/models"
	"common/node_pieces"
	"common/pb"
	"common/rs"
	"common/uuid"
	"encoding/json"
	"github.com/wuzfei/go-helper/maps"
	"go.uber.org/zap"
)
import "context"

//GetTask(ctx context.Context, in *TaskRequest) (*TaskResponse, error)

// GetTask 获取一个修复任务
func (s *MetainfoServer) GetTask(ctx context.Context, req *pb.TaskRequest) (*pb.TaskResponse, error) {
	nodeId, err := uuid.FromBytes(req.NodeId)
	if err != nil {
		return nil, err
	}
	obj, seg, rep, err := s.db.GetRepairTask(ctx, nodeId)
	if err != nil {
		s.log.Info("没有获取到修复任务", zap.String("nodeId", nodeId.String()), zap.Error(err))
		//没有获取到修复任务
		return &pb.TaskResponse{
			Empty: true,
		}, nil
	}

	downloadInfoPieces := make([]*pb.NodePieces, 0)
	uploadInfo := make([]*pb.NodeInfo, 0)

	//获取下载的节点信息
	nodeIdxs := maps.Values(seg.NodePieces)
	nodes, err := s.db.GetNodesByIdxs(ctx, nodeIdxs)
	if err != nil {
		return nil, err
	}
	nodeMaps := make(map[uint32]*models.Node)
	for i := range nodes {
		nodeMaps[nodes[i].IDx] = nodes[i]
	}
	for _idx, _nodeIdx := range seg.NodePieces {
		var _nodeInfo *models.Node
		var ok bool
		if _nodeInfo, ok = nodeMaps[_nodeIdx]; !ok {
			continue
		}
		downloadInfoPieces = append(downloadInfoPieces, &pb.NodePieces{
			Index: int32(_idx),
			NodeInfo: &pb.NodeInfo{
				NodeId: _nodeInfo.ID.Bytes(),
				StorageNodeAddress: &pb.NodeAddress{
					Ip:       _nodeInfo.Ip,
					Ipv6:     _nodeInfo.Ipv6,
					DeviceId: _nodeInfo.DevId,
					Port:     int32(_nodeInfo.Port),
					Nat:      int32(_nodeInfo.Nat),
				},
				PeerFwd: int32(_nodeInfo.Fwd),
			},
		})
	}

	//获取上传的节点信息
	excludeNodeIdxs := make([]uint32, 0)
	if !s.config.AllowExistsNode {
		excludeNodeIdxs = nodeIdxs
	}
	nodes, err = s.db.GetUploadNodesByWeight(ctx, &db.NodeSelection{
		Quantity:       int(obj.Redundancy.Total),
		ExcludeNodeIdx: excludeNodeIdxs,
	})
	if err != nil {
		return nil, err
	}
	for i := range nodes {
		uploadInfo = append(uploadInfo, &pb.NodeInfo{
			NodeId: nodes[i].ID.Bytes(),
			StorageNodeAddress: &pb.NodeAddress{
				Ip:       nodes[i].Ip,
				Ipv6:     nodes[i].Ipv6,
				DeviceId: nodes[i].DevId,
				Port:     int32(nodes[i].Port),
				Nat:      int32(nodes[i].Nat),
			},
			PeerFwd: int32(nodes[i].Fwd),
		})
	}
	//上传节点太少，就没必要了
	if len(uploadInfo) <= int(obj.Redundancy.Repair) {
		s.log.Error("GetTask 上传节点太少，就没必要了",
			zap.Int("uploadInfoLenght", len(uploadInfo)), zap.Uint32("repair", obj.Redundancy.Repair))
		return &pb.TaskResponse{
			Empty: true,
		}, nil
	}

	//拼装返回数据
	exp := int64(0)
	if obj.ExpireTime.Valid {
		exp = obj.ExpireTime.Time.Unix()
	}
	resp := &pb.TaskResponse{
		Empty:      false,
		StreamId:   rep.StreamId.Bytes(),
		Rs:         rs.ToPbRs(obj.Redundancy),
		ExpireTime: exp,
		DownloadInfo: &pb.SegmentDownload{
			Index:       int32(seg.Position),
			Padding:     int32(seg.Pad),
			SegmentSize: seg.PlainSize,
			Pieces:      downloadInfoPieces,
		},
		UploadInfo: uploadInfo,
	}
	_r, _ := json.Marshal(resp)
	s.log.Info("GetTask", zap.String("nodeId", nodeId.String()), zap.ByteString("resp", _r))
	return resp, nil
}

// CommitTask 提交修复任务结果
func (s *MetainfoServer) CommitTask(ctx context.Context, req *pb.CommitTaskRequest) (*pb.CommitTaskResponse, error) {
	//更新 segment 表中字段
	streamId, err := uuid.FromBytes(req.StreamId)
	if err != nil {
		s.log.Error("", zap.Error(err))
		return nil, err
	}
	nodePieces := node_pieces.NodePieces{}
	for _, info := range req.Infos {
		_nid, _ := uuid.FromBytes(info.NodeId)
		nodePieces[uint8(info.Index)] = s.db.NodeIdToIdx(ctx, _nid)
	}
	err = s.db.CommitRepairTask(ctx, streamId, req.SegmentIndex, nodePieces)
	return &pb.CommitTaskResponse{}, err
}
